/*
  Meanwhile - Unofficial Lotus Sametime client library
  
  Copyright (c) 2007 Christopher O'Brien <siege@preoccupied.net>
  
  License: LGPL
  
  This library is free software; you can redistribute it and/or modify
  it under the terms of version 2 the GNU Library General Public
  License as published by the Free Software Foundation.
                                                                                
  This library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  USA
*/


#include "mw-queue.h"
#include <glist.h>
#include <ghash.h>


struct mw_queue {
  GSList *head;
  GSList *tail;
  GDestroyNotify clear;
};


MwQueue *mw_queue_new(GDestroyNotify clear_item) {
  MwQueue *queue = g_new0(MwQueue, 1);
  queue->clear = clear_item;
  return queue;
}


void mw_queue_clear(MwQueue *queue) {
  g_return_if_fail(queue != NULL);

  if(queue->clear) {
    GSList *l;
    for(l = queue->head; l; l = l->next) {
      queue->clear(l->data);
    }
  }

  g_slist_free(queue->head);
  queue->head = NULL;
  queue->tail = NULL;
}


void mw_queue_free(MwQueue *queue) {
  if(queue) {
    mw_queue_clear(queue);
    g_free(queue);
  }
}


void mw_queue_add(MwQueue *queue, gpointer item) {
  g_return_if_fail(queue != NULL);

  if(queue->head) {
    g_slist_append(queue->tail, item);
    queue->tail = queue->tail->next;

  } else {
    queue->head = g_slist_append(NULL, item);
    queue->tail = queue->head;
  }
}


gpointer mw_queue_current(MwQueue *queue) {
  g_return_val_if_fail(queue != NULL, NULL);
  return queue->head? queue->head->data: NULL;
}


gpointer mw_queue_next(MwQueue *queue) {
  g_return_if_fail(queue != NULL);

  if(queue->head) {

    if(queue->clear) {
      queue->clear(queue->head->data);
    }

    queue->head = g_slist_delete_link(queue->head, queue->head);

    if(! queue->head) {
      queue->tail = NULL;
    }
  }

  return queue->head? queue->head->data: NULL;
}


guint mw_queue_count(MwQueue *queue) {
  g_return_val_if_fail(queue != NULL, 0);
  return g_slist_length(queue->head);
}


struct mw_keyed_queue {
  GHashTable *table;
  GSList *head;
  GSList *tail;
  GDestroyNotify clear;
};


MwKeyedQueue *mw_keyed_queue_new(GDestroyNotify clear_item,
				 GHashFunc hash_key, GEqualFunc equal_key,
				 GDestroyNotify clear_key) {

  MwKeyedQueue *queue = g_new0(MwKeyedQueue, 1);
  queue->table = g_hash_table_new_full(hash_key, equal_key, clear_key,
				       (GDestroyNotify) mw_queue_free);
  queue->clear = clear_item;
}


void mw_keyed_queue_clear(MwKeyedQueue *queue) {
  g_return_if_fail(queue != NULL);

  g_slist_free(queue->head);
  queue->head = NULL;
  queue->tail = NULL;

  g_hash_table_remove_all(queue->table);
}


void mw_keyed_queue_clear_key(MwKeyedQueue *queue, gpointer key) {
  MwQueue *keyqueue;
  GList *spot;

  g_return_if_fail(queue != NULL);

  /* find the key's queue */
  keyqueue = g_hash_table_lookup(queue->table, key);
  g_return_if_fail(keyqueue != NULL);

  /* remove the key's queue from the list */
  queue->head = g_slist_remove(queue->head, keyqueue);
  queue->tail = g_slist_last(queue->head);

  /* remove the key and it's queue from the table */
  g_hash_table_remove(queue->table, key);
}


void mw_keyed_queue_free(MwKeyedQueue *queue) {
  if(queue) {
    mw_keyed_queue_clear(queue);
    g_hash_table_destroy(queue->table);
    g_free(queue);
  }
}


void mw_keyed_queue_add(MwKeyedQueue *queue, gpointer key, gpointer item) {
  MwQueue *keyqueue;
  
  g_return_if_fail(queue != NULL);

  keyqueue = g_hash_table_lookup(queue->table, key);

  if(! keyqueue) {
    keyqueue = mw_queue_new(queue->clear);
    g_hash_table_insert(key, keyqueue);
  }

  mw_queue_add(keyqueue, item);

  if(keyqueue->head == keyqueue->tail) {
    /* if the key's queue only has one item in it, therefore it should
       not already be in the list, so we need to append it */

    if(queue->head) {
      g_slist_append(queue->tail, keyqueue);
      queue->tail = queue->tail->next;

    } else {
      queue->head = g_slist_append(NULL, keyqueue);
      queue->tail = queue->head;
    }
  }
}


gpointer mw_keyed_queue_current(MwKeyedQueue *queue) {
  g_return_val_if_fail(queue != NULL, NULL);
  return (queue->head)? mw_queue_current(queue->head->data): NULL;
}


gpointer mw_keyed_queue_next(MwKeyedQueue *queue) {
  g_return_if_fail(queue != NULL);

  if(queue->head) {
    MwQueue *q;
    q = queue->head->data;
    mw_queue_next(q);

    if(! q->head) {
      /* remove q from list */
      queue->head = g_slist_delete_link(queue->head, queue->head);
      if(! queue->head) {
	queue->tail = NULL;
      }

    } else if(queue->head->next) {
      /* rotate q to end of list */
      queue->tail->next = queue->head;
      queue->head = queue->head->next;
      queue->tail = queue->tail->next;
      queue->tail->next = NULL;

    } else {
      /* q is the only entry in list */
      ;
    }
  }

  return queue->head? mw_queue_current(queue->head->data): NULL;
}


guint mw_keyed_queue_count_key(MwKeyedQueue *queue, gpointer key) {
  MwQueue *keyqueue;

  g_return_val_if_fail(queue != NULL, 0);

  keyqueue = g_hash_table_lookup(queue->table, key);
  return keyqueue? mw_queue_count(keyqueue): 0;
}


guint mw_keyed_queue_count(MwKeyedQueue *queue) {
  GSList *l;
  guint count = 0;

  g_return_val_if_fail(queue != NULL, 0);

  for(l = queue->head; l; l = l->next) {
    count += mw_queue_count(l->data);
  }

  return count;
}


/* The end. */
